package lock;

/**
 * Created by lizhenyu on 2019/9/23
 *
 * description 模拟读写锁
 */
public class RWLock {
    private int readers = 0;
    private int writers = 0;
    private int writerWaitors = 0;

    ReadLockThread readerThread;
    WriteLockThread writerThread;

    public RWLock() {
        readerThread = new ReadLockThread(3);
        writerThread = new WriteLockThread(2);
    }

    /**
     * 加读锁
     */
    public synchronized void lockRead() throws InterruptedException {
        // 读锁持有者可以有多个，但是必须在没有写者情况下获得
        if(writers > 0 || writerWaitors > 0) {
            show("被阻塞");
            wait();
        }
        readers++;
        show("lockRead");
    }

    /**
     * 解读锁
     */
    public synchronized void unLockRead() {
        readers--;
        notifyAll();
        show("unLockRead");
    }

    /**
     * 加写锁
     */
    public synchronized void lockWrite() throws InterruptedException {
        writerWaitors++;
        // 写锁持有者只能有一个，但是必须在没有写者和读者的情况下获得
        if(readers > 0 || writers > 0) {
            show("被阻塞");
            wait();
        }
        writerWaitors--;
        writers++;
        show("lockWrite");
    }

    /**
     * 解写锁
     */
    public synchronized void unLockWrite() {
        writers--;
        notifyAll();
        show("unLockWrite");
    }

    class ReadLockThread extends Thread {
        int second;
        ReadLockThread(int second) {
            this.second = second;
        }
        @Override
        public void run() {
            try {
                while (true) {
                    lockRead();
                    sleep(second * 1000);
                    unLockRead();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void show(String str) {
        System.out.println(Thread.currentThread().getName() + " -- " + str + " -- " + readers + " " + writers);
    }

    class WriteLockThread extends Thread {
        int second;
        WriteLockThread(int second) {
            this.second = second;
        }
        @Override
        public void run() {
            try {
                while (true) {
                    lockWrite();
                    sleep(second * 1000);
                    unLockWrite();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        RWLock lock = new RWLock();
        lock.writerThread.start();
        lock.readerThread.start();
    }

}
